package org.infodavid.common.model.impl;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;

import org.infodavid.common.persistence.IDataObject;
import org.joda.time.DateTime;

/**
 * The Class AbstractDataObjectImpl.
 * @param <K> the key type
 */
public abstract class AbstractDataObjectImpl<K extends Serializable> extends
    AbstractBasicDataObjectImpl implements IDataObject<K> {

  /** serialVersionUID. */
  private static final long serialVersionUID = -1682330418928589183L;

  /** The key. */
  private K key;

  /** The creation date. */
  private DateTime creationDate;

  /** The modification date. */
  private DateTime modificationDate;

  /** The archived. */
  private boolean archived;

  /** The property change Listener. */
  private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

  /**
   * constructor.
   * @param keyArg the key
   * @throws Exception the exception
   */
  public AbstractDataObjectImpl(final K keyArg) throws Exception {
    super();

    setKey(keyArg);
  }

  /**
   * constructor.
   * @throws Exception the exception
   */
  protected AbstractDataObjectImpl() throws Exception {
    super();
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @return the creation date
   * @see org.infodavid.common.persistence.IDataObject#getCreationDate()
   */
  @Override
  public DateTime getCreationDate() {
    return creationDate;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @return the modification date
   * @see org.infodavid.common.persistence.IDataObject#getModificationDate()
   */
  @Override
  public DateTime getModificationDate() {
    return modificationDate;
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.model.impl.SimpleDataObjectImpl#init()
   */
  @Override
  public void init() throws Exception {
    super.init();

    archived = false;
    creationDate = new DateTime();
    modificationDate = creationDate;
    key = null;
  }

  /*
   * (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  @Override
  public abstract int hashCode();

  /*
   * (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public abstract boolean equals(Object obj);

  /**
   * See super class or interface. (non-Javadoc)
   * @return the key
   * @see org.infodavid.common.persistence.IDataObject#getKey()
   */
  @Override
  public K getKey() {
    return key;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @return true, if checks if is archived
   * @see org.infodavid.common.persistence.IDataObject#isArchived()
   */
  @Override
  public boolean isArchived() {
    return archived;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @param flag the archived
   * @see org.infodavid.common.persistence.IDataObject#setArchived(boolean)
   */
  @Override
  public void setArchived(final boolean flag) {
    if (archived != flag) {
      archived = flag;
      markModified(ARCHIVED, Boolean.valueOf(!flag), Boolean.valueOf(flag));
    }
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @param value the creation date
   * @see org.infodavid.common.persistence.IDataObject#setCreationDate(org.joda.time.DateTime)
   */
  public void setCreationDate(final DateTime value) {
    if (!equals(creationDate, value)) {
      final Object old = creationDate;
      creationDate = value;
      markModified(CREATION_DATE, old, creationDate);

      if (getModificationDate() == null) {
        setModificationDate(value);
      }
    }
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @param value the modification date
   * @see org.infodavid.common.persistence.IDataObject#setModificationDate(org.joda.time.DateTime)
   */
  @Override
  public void setModificationDate(final DateTime value) {
    if (!equals(modificationDate, value)) {
      final Object old = modificationDate;
      modificationDate = value;
      markModified(MODIFICATION_DATE, old, modificationDate);
    }
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.model.IDataObject#markModified(java.lang.String, java.lang.Object,
   * java.lang.Object)
   */
  @Override
  public void markModified(final String property, final Object oldValue, final Object newValue) {
    if (isModificationTrackingEnabled()) {
      super.markModified(property);
      getChangeSupport().fireIndexedPropertyChange(property, 0, oldValue, newValue);
    }
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.model.IDataObject#markModified(java.beans.PropertyChangeEvent)
   */
  @Override
  public void markModified(final PropertyChangeEvent event) {
    if (isModificationTrackingEnabled()) {
      if (event != null) {
        super.markModified(event.getPropertyName());
        getChangeSupport().firePropertyChange(event);
      }
    }
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @param buffer the buffer
   * @return the string buffer
   * @see org.infodavid.common.model.impl.AbstractBasicDataObjectImpl.impl.BasicDataObjectImpl#toString(java.lang.StringBuffer)
   */
  @Override
  protected StringBuffer toString(final StringBuffer buffer) {
    super.toString(buffer);

    buffer.append(',');
    buffer.append("key = ").append(getKey());
    buffer.append(',');
    buffer.append("isArchived = ").append(isArchived());
    buffer.append(',');
    buffer.append("modificationDate = ").append(getModificationDate());

    return buffer;
  }

  /**
   * See super class or interface. (non-Javadoc)
   * @param value the key
   * @see org.infodavid.common.persistence.IDataObject#setKey(java.io.Serializable)
   */
  @Override
  public void setKey(final K value) {
    if (!equals(key, value)) {
      final Object old = key;
      key = value;
      markModified(KEY, old, value);
    }
  }

  /**
   * Gets the change support.
   * @return the change support
   */
  public PropertyChangeSupport getChangeSupport() {
    return changeSupport;
  }
}
